﻿Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler

Namespace System.Workflow.Activities
    <ActivityValidator(GetType(EventHandlersValidator)), ToolboxBitmap(GetType(EventHandlersActivity), "Resources.events.png"), Designer(GetType(EventHandlersDesigner), GetType(IDesigner)), SRCategory("Standard"), AlternateFlowActivity, ToolboxItem(False)> _
    Public NotInheritable Class EventHandlersActivity
        Inherits CompositeActivity
        Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs)
        ' Methods
        Public Sub New()
        End Sub

        Public Sub New(ByVal name As String)
            MyBase.New(name)
        End Sub

        Private Function AllHandlersAreQuiet(ByVal handlers As EventHandlersActivity, ByVal context As ActivityExecutionContext) As Boolean
            Dim executionContextManager As ActivityExecutionContextManager = context.ExecutionContextManager
            Dim i As Integer
            For i = 0 To handlers.EnabledActivities.Count - 1
                Dim activity As EventDrivenActivity = [TryCast](handlers.EnabledActivities.Item(i),EventDrivenActivity)
                If ((Not executionContextManager.GetExecutionContext(activity) Is Nothing) OrElse ((Not handlers.ActivityState Is Nothing) AndAlso (handlers.ActivityState.Item(i).PendingExecutionCount > 0))) Then
                    Return False
                End If
            Next i
            Return True
        End Function

        Protected Overrides Function Cancel(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (Me.ActivityState Is Nothing) Then
                Return ActivityExecutionStatus.Closed
            End If
            Dim isScopeCompleted As Boolean = Me.IsScopeCompleted
            Dim flag2 As Boolean = True
            Dim i As Integer
            For i = 0 To MyBase.EnabledActivities.Count - 1
                Dim activity As EventDrivenActivity = [TryCast](MyBase.EnabledActivities.Item(i),EventDrivenActivity)
                Dim parentEventHandler As EventHandlerEventActivitySubscriber = Me.ActivityState.Item(i)
                parentEventHandler.PendingExecutionCount = 0
                Dim context As ActivityExecutionContext = executionContext.ExecutionContextManager.GetExecutionContext(activity)
                If (Not context Is Nothing) Then
                    Select Case context.Activity.ExecutionStatus
                        Case ActivityExecutionStatus.Executing
                            context.CancelActivity(context.Activity)
                            flag2 = False
                            Exit Select
                        Case ActivityExecutionStatus.Canceling, ActivityExecutionStatus.Faulting
                            flag2 = False
                            Exit Select
                    End Select
                End If
                If Not isScopeCompleted Then
                    activity.EventActivity.Unsubscribe(executionContext, parentEventHandler)
                End If
            Next i
            If flag2 Then
                Me.ActivityState = Nothing
                Return ActivityExecutionStatus.Closed
            End If
            Return MyBase.ExecutionStatus
        End Function

        Protected Overrides Function Execute(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            Dim list As New List([Of] EventHandlerEventActivitySubscriber)
            Me.ActivityState = list
            Dim i As Integer
            For i = 0 To MyBase.EnabledActivities.Count - 1
                Dim eventDriven As EventDrivenActivity = [TryCast](MyBase.EnabledActivities.Item(i),EventDrivenActivity)
                Dim item As New EventHandlerEventActivitySubscriber(eventDriven)
                list.Add(item)
                eventDriven.EventActivity.Subscribe(executionContext, item)
            Next i
            Return ActivityExecutionStatus.Executing
        End Function

        Public Function GetDynamicActivity(ByVal childActivityName As String) As Activity
            If (childActivityName Is Nothing) Then
                Throw New ArgumentNullException("childActivityName")
            End If
            Dim childActivity As Activity = Nothing
            Dim i As Integer
            For i = 0 To MyBase.EnabledActivities.Count - 1
                If MyBase.EnabledActivities.Item(i).QualifiedName.Equals(childActivityName) Then
                    childActivity = MyBase.EnabledActivities.Item(i)
                    Exit For
                End If
            Next i
            If (childActivity Is Nothing) Then
                Throw New ArgumentException(SR.GetString("Error_EventHandlersChildNotFound"), "childActivityName")
            End If
            Return Me.GetDynamicActivity(childActivity)
        End Function

        <Browsable(False), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
        Private Function GetDynamicActivity(ByVal childActivity As Activity) As Activity
            If (childActivity Is Nothing) Then
                Throw New ArgumentNullException("childActivity")
            End If
            If Not MyBase.EnabledActivities.Contains(childActivity) Then
                Throw New ArgumentException(SR.GetString("Error_EventHandlersChildNotFound"), "childActivity")
            End If
            Dim dynamicActivities As Activity() = MyBase.GetDynamicActivities(childActivity)
            If (dynamicActivities.Length <> 0) Then
                Return dynamicActivities(0)
            End If
            Return Nothing
        End Function

        Protected Overrides Sub Initialize(ByVal provider As IServiceProvider)
            If (MyBase.Parent Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_MustHaveParent"))
            End If
            MyBase.Initialize(provider)
        End Sub

        Protected Overrides Sub OnActivityChangeAdd(ByVal executionContext As ActivityExecutionContext, ByVal addedActivity As Activity)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (addedActivity Is Nothing) Then
                Throw New ArgumentNullException("addedActivity")
            End If
            Dim eventDriven As EventDrivenActivity = [TryCast](addedActivity,EventDrivenActivity)
            Dim activity As EventHandlersActivity = DirectCast(executionContext.Activity, EventHandlersActivity)
            Dim parentEventHandler As New EventHandlerEventActivitySubscriber(eventDriven)
            If (((activity.ExecutionStatus = ActivityExecutionStatus.Executing) AndAlso (Not activity.ActivityState Is Nothing)) AndAlso Not activity.IsScopeCompleted) Then
                eventDriven.EventActivity.Subscribe(executionContext, parentEventHandler)
                activity.ActivityState.Insert(activity.EnabledActivities.IndexOf(addedActivity), parentEventHandler)
            End If
        End Sub

        Protected Overrides Sub OnActivityChangeRemove(ByVal executionContext As ActivityExecutionContext, ByVal removedActivity As Activity)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (removedActivity Is Nothing) Then
                Throw New ArgumentNullException("removedActivity")
            End If
            Dim activity As EventDrivenActivity = [TryCast](removedActivity,EventDrivenActivity)
            Dim activity2 As EventHandlersActivity = DirectCast(executionContext.Activity, EventHandlersActivity)
            If (((activity2.ExecutionStatus = ActivityExecutionStatus.Executing) AndAlso (Not activity2.ActivityState Is Nothing)) AndAlso Not activity2.IsScopeCompleted) Then
                Dim i As Integer
                For i = 0 To activity2.ActivityState.Count - 1
                    Dim parentEventHandler As EventHandlerEventActivitySubscriber = activity2.ActivityState.Item(i)
                    If parentEventHandler.eventDrivenActivity.QualifiedName.Equals(removedActivity.QualifiedName) Then
                        activity.EventActivity.Unsubscribe(executionContext, parentEventHandler)
                        activity2.ActivityState.RemoveAt(i)
                        Return
                    End If
                Next i
            End If
        End Sub

        Protected Overrides Sub OnClosed(ByVal provider As IServiceProvider)
            MyBase.RemoveProperty(EventHandlersActivity.ActivityStateProperty)
            MyBase.RemoveProperty(EventHandlersActivity.IsScopeCompletedProperty)
        End Sub

        Private Sub OnUnsubscribeAndClose(ByVal sender As Object, ByVal args As EventArgs)
            If (sender Is Nothing) Then
                Throw New ArgumentNullException("sender")
            End If
            If (args Is Nothing) Then
                Throw New ArgumentNullException("args")
            End If
            Dim parentContext As ActivityExecutionContext = DirectCast(sender, ActivityExecutionContext)
            If (parentContext Is Nothing) Then
                Throw New ArgumentException("sender")
            End If
            Dim activity As EventHandlersActivity = [TryCast](parentContext.Activity,EventHandlersActivity)
            If (parentContext.Activity.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                activity.IsScopeCompleted = True
                Dim executionContextManager As ActivityExecutionContextManager = parentContext.ExecutionContextManager
                Dim flag As Boolean = True
                Dim i As Integer
                For i = 0 To activity.EnabledActivities.Count - 1
                    Dim activity2 As EventDrivenActivity = [TryCast](activity.EnabledActivities.Item(i),EventDrivenActivity)
                    Dim parentEventHandler As EventHandlerEventActivitySubscriber = activity.ActivityState.Item(i)
                    activity2.EventActivity.Unsubscribe(parentContext, parentEventHandler)
                    If ((Not executionContextManager.GetExecutionContext(activity2) Is Nothing) OrElse (activity.ActivityState.Item(i).PendingExecutionCount <> 0)) Then
                        flag = False
                    End If
                Next i
                If flag Then
                    activity.ActivityState = Nothing
                    parentContext.CloseActivity
                End If
            End If
        End Sub

        Protected Overrides Sub OnWorkflowChangesCompleted(ByVal executionContext As ActivityExecutionContext)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            MyBase.OnWorkflowChangesCompleted(executionContext)
            If (Not Me.ActivityState Is Nothing) Then
                Select Case MyBase.ExecutionStatus
                    Case ActivityExecutionStatus.Executing
                        If (Me.IsScopeCompleted AndAlso Me.AllHandlersAreQuiet(Me, executionContext)) Then
                            executionContext.CloseActivity
                        End If
                        Return
                    Case ActivityExecutionStatus.Canceling, ActivityExecutionStatus.Faulting
                        If Me.AllHandlersAreQuiet(Me, executionContext) Then
                            executionContext.CloseActivity
                        End If
                        Return
                    Case ActivityExecutionStatus.Closed, ActivityExecutionStatus.Compensating
                        Return
                End Select
            End If
        End Sub

        Private Sub System.Workflow.ComponentModel.IActivityEventListener<System.Workflow.ComponentModel.ActivityExecutionStatusChangedEventArgs>.OnEvent(ByVal sender As Object, ByVal e As ActivityExecutionStatusChangedEventArgs) Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs).OnEvent
            If (sender Is Nothing) Then
                Throw New ArgumentNullException("sender")
            End If
            If (e Is Nothing) Then
                Throw New ArgumentNullException("e")
            End If
            Dim context As ActivityExecutionContext = [TryCast](sender,ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender")
            End If
            Dim activity As EventDrivenActivity = [TryCast](e.Activity,EventDrivenActivity)
            Dim handlers As EventHandlersActivity = [TryCast](context.Activity,EventHandlersActivity)
            e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, Me)
            Dim executionContextManager As ActivityExecutionContextManager = context.ExecutionContextManager
            executionContextManager.CompleteExecutionContext(executionContextManager.GetExecutionContext(activity))
            Select Case handlers.ExecutionStatus
                Case ActivityExecutionStatus.Executing
                    Dim i As Integer
                    For i = 0 To handlers.EnabledActivities.Count - 1
                        If handlers.EnabledActivities.Item(i).QualifiedName.Equals(activity.QualifiedName) Then
                            Dim subscriber As EventHandlerEventActivitySubscriber = handlers.ActivityState.Item(i)
                            If (subscriber.PendingExecutionCount > 0) Then
                                subscriber.PendingExecutionCount -= 1
                                subscriber.IsBlocked = False
                                Dim context2 As ActivityExecutionContext = executionContextManager.CreateExecutionContext(handlers.EnabledActivities.Item(i))
                                context2.Activity.RegisterForStatusChange(Activity.ClosedEvent, Me)
                                context2.ExecuteActivity(context2.Activity)
                                Return
                            End If
                            subscriber.IsBlocked = True
                            If (handlers.IsScopeCompleted AndAlso Me.AllHandlersAreQuiet(handlers, context)) Then
                                context.CloseActivity
                                Return
                            End If
                            Exit For
                        End If
                    Next i
                    Return
                Case ActivityExecutionStatus.Canceling, ActivityExecutionStatus.Faulting
                    If Me.AllHandlersAreQuiet(handlers, context) Then
                        context.CloseActivity
                    End If
                    Exit Select
                Case ActivityExecutionStatus.Closed, ActivityExecutionStatus.Compensating
                    Exit Select
                Case Else
                    Return
            End Select
        End Sub

        Friend Sub UnsubscribeAndClose()
            MyBase.Invoke([Of] EventArgs)(New EventHandler([Of] EventArgs)(AddressOf Me.OnUnsubscribeAndClose), EventArgs.Empty)
        End Sub


        ' Properties
        Private Property ActivityState As List([Of] EventHandlerEventActivitySubscriber)
            Get
                Return DirectCast(MyBase.GetValue(EventHandlersActivity.ActivityStateProperty), List([Of] EventHandlerEventActivitySubscriber))
            End Get
            Set(ByVal value As List([Of] EventHandlerEventActivitySubscriber))
                If (value Is Nothing) Then
                    MyBase.RemoveProperty(EventHandlersActivity.ActivityStateProperty)
                Else
                    MyBase.SetValue(EventHandlersActivity.ActivityStateProperty, value)
                End If
            End Set
        End Property

        Private Property IsScopeCompleted As Boolean
            Get
                Return CBool(MyBase.GetValue(EventHandlersActivity.IsScopeCompletedProperty))
            End Get
            Set(ByVal value As Boolean)
                MyBase.SetValue(EventHandlersActivity.IsScopeCompletedProperty, value)
            End Set
        End Property


        ' Fields
        Private Shared ActivityStateProperty As DependencyProperty = DependencyProperty.Register("ActivityState", GetType(List([Of] EventHandlerEventActivitySubscriber)), GetType(EventHandlersActivity))
        Private Shared IsScopeCompletedProperty As DependencyProperty = DependencyProperty.Register("IsScopeCompleted", GetType(Boolean), GetType(EventHandlersActivity), New PropertyMetadata(False))

        ' Nested Types
        <Serializable> _
        Private NotInheritable Class EventHandlerEventActivitySubscriber
            Implements IActivityEventListener([Of] QueueEventArgs)
            ' Methods
            Friend Sub New(ByVal eventDriven As EventDrivenActivity)
                Me.eventDrivenActivity = eventDriven
            End Sub

            Private Sub System.Workflow.ComponentModel.IActivityEventListener<System.Workflow.ComponentModel.QueueEventArgs>.OnEvent(ByVal sender As Object, ByVal e As QueueEventArgs) Implements IActivityEventListener([Of] QueueEventArgs).OnEvent
                If (sender Is Nothing) Then
                    Throw New ArgumentNullException("sender")
                End If
                If (e Is Nothing) Then
                    Throw New ArgumentNullException("e")
                End If
                Dim context As ActivityExecutionContext = [TryCast](sender,ActivityExecutionContext)
                If (context Is Nothing) Then
                    Throw New ArgumentException("sender")
                End If
                Dim activityStatusChangeListener As EventHandlersActivity = [TryCast](context.Activity,EventHandlersActivity)
                If ((activityStatusChangeListener.ExecutionStatus = ActivityExecutionStatus.Executing) AndAlso activityStatusChangeListener.EnabledActivities.Contains(Me.eventDrivenActivity)) Then
                    If Me.IsBlocked Then
                        Me.IsBlocked = False
                        Dim context2 As ActivityExecutionContext = context.ExecutionContextManager.CreateExecutionContext(Me.eventDrivenActivity)
                        context2.Activity.RegisterForStatusChange(Activity.ClosedEvent, activityStatusChangeListener)
                        context2.ExecuteActivity(context2.Activity)
                    Else
                        Me.PendingExecutionCount += 1
                    End If
                End If
            End Sub


            ' Properties
            Friend Property IsBlocked As Boolean
                Get
                    Return Me.isBlocked
                End Get
                Set(ByVal value As Boolean)
                    Me.isBlocked = value
                End Set
            End Property

            Friend Property PendingExecutionCount As Integer
                Get
                    Return Me.numOfMsgs
                End Get
                Set(ByVal value As Integer)
                    Me.numOfMsgs = value
                End Set
            End Property


            ' Fields
            Friend eventDrivenActivity As EventDrivenActivity
            Private isBlocked As Boolean = True
            Private numOfMsgs As Integer = 0
        End Class
    End Class
End Namespace

